#pragma once

#include <Windows.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

#include <GL\GL.h>
#include <GL\GLU.h>
#include <GL\glut.h>

#include <iostream>
#include <string>

#include "RigidBody.h"
#include "Ball.h"
#include "Bar.h"
#include "Board.h"
#include "HingeBar.h"
#include "BezierCurve.h"
#include "Collision.h"
#include "SquareBumper.h"
#include "TriangleBumper.h"
#include "TextField.h"

using namespace std;

#define window_width 600
#define window_height 800
#define BOARD_X 24
#define BOARD_Y 32
#define aspectRatio window_width/window_height

// Variables for keeping track of input
bool keys[256];
bool mouse[3];
vector3 mousePos;

int playerScore = 0;
char * textMessage = "Hello World";

// Variables for keeping track of time
double freq;
LARGE_INTEGER tick;

double currentTime = 0;
double lastTime = 0;
double deltaTime = 0;
double dtMax = 1.0/60.0;

vector3 scoreboardPos = vector3( TOP_RIGHT_POINT.getX() - 100, TOP_RIGHT_POINT.getY(), TOP_RIGHT_POINT.getZ());
TextField scoreBoard = TextField( scoreboardPos, WHITE, "Current Score", "0");
TextField gameOverBoard = TextField( vector3( 200,500, 0), YELLOW, "", "" );
TextField ballsLeftBoard = TextField( vector3( 10, TOP_Y, 0), WHITE, "Balls Left", "" );
// Paddles and ball
Ball ball;
int ballsleft = 3;
Board board;
HingeBar pLeft;
HingeBar pRight;

TriangleBumper tbump1;
TriangleBumper tbump2;

// Board Structure
Bar bBase;
Bar bRight;
Bar bLeft;
Bar bTop;
Bar bTopRight;
Bar bShute;
Bar bLeftGuard;
Bar bLeftHook;
Bar bRightGuard;
Bar bRightHook;
vector<Bar> boardColliders;

// Constants
double pi = 3.1415;
double winW = 600;
double winH = 800;
vector3 gravity = vector3(0, -40, 0);
double paddleStr = 100000;

static void drawAxis()
{
	glLoadIdentity();

	glBegin(GL_LINES);
	glColor3f(1,0,0);
	glVertex3f(winW/2,winH/2,0);
	glVertex3f(winW/2 + 50,winH/2,0);

	glColor3f(0,1,0);
	glVertex3f(winW/2,winH/2,0);
	glVertex3f(winW/2,winH/2 + 50,0);

	glColor3f(0,0,1);
	glVertex3f(winW/2,winH/2,0);
	glVertex3f(winW/2,winH/2,50);
	glEnd(); 

    glEnd(); 
}

static void drawObjects()
{
	/*
	glLoadIdentity();
	vector3 * leftRollOut = new vector3[103];
	
	generateBezierCurve( vector3( LEFT_X, 200, 0 ), BOTTOM_LEFT_POINT,BOTTOM_LEFT_POINT,
		vector3( 180, BOTTOM_Y, 0 ), leftRollOut );
	leftRollOut[100] = vector3( 180, BOTTOM_Y, 0 ); 
	leftRollOut[101] = BOTTOM_LEFT_POINT;
	leftRollOut[102] = vector3( LEFT_X, 100, 0 );

	DrawTools::drawCurvePoints( leftRollOut, 100, GREEN, 2 );*/

	//schuteGuide.Render();
	tbump1.Render();
	tbump2.Render();
}

static void renderColliders()
{
	for (int i = 0; i < boardColliders.size(); i++)
	{
		boardColliders[i].Render();
	}
}

static void keyboardFunc(unsigned char key, int x, int y)
{
	keys[key] = true;
}

static void keyUpFunc(unsigned char key, int x, int y)
{
	keys[key] = false;
}

static void motionFunc(int x, int y)
{
	ball.transform.position.x = x;
	ball.transform.position.y = winH - y;
}

static void reshape( GLsizei w, GLsizei h ){
	if( h == 0 )
		h = 1; //Prevent Divide by Zero

	glViewport (0, 0,w, h );
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
    
	//gluPerspective(45.0f, (GLfloat) w/(GLfloat) h, 0.1f, 100.0f);
	gluOrtho2D(0.0f, (GLfloat)winW, 0.0f, (GLfloat)winH);
    
	glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

    gluLookAt (0.0, 0.0, 80 , 0.0, 0.0, 0.0, 0.0, 1.0, 100.0);
}

static void Input(void)
{
	if (keys['q'])
	{
		exit(0);
	}

	if (keys['z'])
	{
		pLeft.transform.torque += vector3::In() * paddleStr;
	}
	else
	{
		pLeft.transform.torque += vector3::In() * -paddleStr;
	}

	if (keys['/'])
	{
		pRight.transform.torque += vector3::In() * -paddleStr;
	}
	else
	{
		pRight.transform.torque += vector3::In() * paddleStr;
	}
	if( keys['r'] ){
		playerScore = 0;
		ballsleft = 3;
		ball = Ball(10, 1);
		ball.transform.position = vector3(530, 70, 0);
		gameOverBoard.clearText();
	}
	if (keys[' '])
	{
		vector3 ballContact;
		if (Collision::TestBallBar(ball, bBase, ballContact))
		{
			ball.transform.force += vector3::Up() * 100000;
		}
	}
}

static void CollisionRoutine(void)
{
	if( ballsleft >= 0){
	// Placeholder vectors
	vector3 ballContact;

	if (Collision::TestBallHinge(ball, pRight, ballContact))
	{
		// Shift the ball so it lies on top of the paddle
		Collision::ShiftBall(ball, ballContact);
		Collision::BounceHinge(ball, pRight, ballContact);
	}
	else if (Collision::TestBallHinge(ball, pLeft, ballContact))
	{
		// Shift the ball so it lies on top of the paddle
		Collision::ShiftBall(ball, ballContact);
		Collision::BounceHinge(ball, pLeft, ballContact);
	}

	for (int i = 0; i < boardColliders.size(); i++)
	{
		if (Collision::TestBallBar(ball, boardColliders[i], ballContact))
		{
			Collision::ShiftBall(ball, ballContact);
			Collision::BounceBar(ball, boardColliders[i], ballContact);
		}
	}

	if( ball.transform.position.getY() < BOTTOM_Y + 2 /*buffer*/ + ball.radius )
	{
			playerScore -= 100;
			ballsleft--;
		
		if( ballsleft < 0){
			//gameover
			gameOverBoard._line1 = "   Game Over, Better Luck Next Time";
			gameOverBoard._line2 = " Want to play again? Hit 'r' to reset";
		}else{
			ball = Ball(10, 1);
			ball.transform.position = vector3(530, 70, 0);
		}
	}
	}
}

static void Update(void)
{
	QueryPerformanceCounter(&tick);
	currentTime = (double)tick.QuadPart/freq;
	deltaTime = (currentTime - lastTime)/3;
	lastTime = currentTime;

	if (deltaTime > dtMax)
		deltaTime = dtMax;
	
	Input();
	CollisionRoutine();

	ball.transform.force += gravity;
	ball.Update(currentTime, deltaTime);
	pLeft.Update(currentTime, deltaTime);
	pRight.Update(currentTime, deltaTime);

	ball.ClearForces();
	pLeft.ClearForces();
	pRight.ClearForces();

	scoreBoard._line2 = TextField::numberToLine(playerScore );
	ballsLeftBoard._line2 = TextField::numberToLine( ballsleft );
	if( ballsleft  > -1 )
		playerScore++;
}

static void displayFunc()
{
	// Update game state
	Update();

	// Clear the screen
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	pLeft.Render();
	pRight.Render();
	ball.Render();
	
	renderColliders();
	scoreBoard.Render();
	gameOverBoard.Render();
	ballsLeftBoard.Render();
	drawObjects();
	board.drawMe();
	//drawAxis();
	
	// Redraw the scene
	glFlush();
	glutSwapBuffers();
	glutPostRedisplay();
}